/*
 * Copyright © 2019-2020  Whale Cloud, Inc. All Rights Reserved.
 *
 * Notice: Whale Cloud Inc copyrights this specification.
 * No part of this specification may be reproduced in any form or means,
 * without the prior written consent of Whale Cloud Inc.
 *
 */
package com.wteam.modules.system.service.impl;

import com.wteam.annotation.Log;
import com.wteam.modules.system.domain.Section;
import com.wteam.modules.system.domain.dto.CourseSubmitDTO;
import com.wteam.modules.system.domain.dto.CusPage;
import com.wteam.modules.system.domain.dto.SectionDTO;
import com.wteam.modules.system.domain.mapper.SectionMapper;
import com.wteam.modules.system.repository.CourseEsRepository;
import com.wteam.modules.system.repository.SectionRepository;
import com.wteam.modules.system.service.CourseService;
import com.wteam.modules.system.domain.Course;
import com.wteam.modules.system.domain.dto.CourseDTO;
import com.wteam.modules.system.domain.criteria.CourseQueryCriteria;
import com.wteam.modules.system.domain.mapper.CourseMapper;
import com.wteam.modules.system.repository.CourseRepository;
import com.wteam.modules.system.util.RandomUtil;
import com.wteam.utils.*;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.cache.annotation.*;
import lombok.RequiredArgsConstructor;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * 课程 业务实现层.
 *
 * @author herry_wong
 * @since 2020-12-28
 */
@Service
@RequiredArgsConstructor
@CacheConfig(cacheNames = "course")
@Transactional(readOnly = true, rollbackFor = Exception.class)
public class CourseServiceImpl implements CourseService {

    private final ElasticsearchRestTemplate elasticsearchRestTemplate;
    private final CourseRepository courseRepository;
    private final CourseEsRepository courseEsRepository;

    private final CourseMapper courseMapper;

    private final RedisUtils redisUtils;

    private final SectionRepository sectionRepository;
    private final SectionMapper sectionMapper;

    @Override
    public Map<String,Object> queryAll(CourseQueryCriteria criteria, Pageable pageable){

        Criteria esCriteria = new Criteria("course");
        if (criteria.getId() != null)esCriteria = esCriteria.and("id").is(criteria.getId());
        if (criteria.getName() != null)esCriteria = esCriteria.and("name").fuzzy(criteria.getName());
        if (criteria.getClassId() != null)esCriteria = esCriteria.and("classId").is(criteria.getClassId());
        if (criteria.getCompulsory() != null)esCriteria = esCriteria.and("compulsory").is(criteria.getCompulsory());
        if (criteria.getLessCredit() != null)esCriteria = esCriteria.and("credit").lessThan(criteria.getLessCredit());
        if (criteria.getGreatCredit() != null)esCriteria = esCriteria.and("credit").greaterThan(criteria.getGreatCredit());
        Query query = new CriteriaQuery(esCriteria);
        Page<CourseDTO> courseDTOPage = elasticsearchRestTemplate.queryForPage(query,CourseDTO.class, IndexCoordinates.of("course"));

        if (courseDTOPage.getContent().size() == 0){
            Page<Course> page = courseRepository.findAll((root, criteriaQuery, criteriaBuilder) ->  QueryHelper.andPredicate(root, criteria, criteriaBuilder), pageable);
            courseDTOPage = page.map(courseMapper::toDto);

            Page<CourseDTO> cusPage = new CusPage<>(courseDTOPage);
            for (CourseDTO courseDTO: cusPage){
                CourseDTO courseDetail = findDTOById(courseDTO.getId());
                Collections.replaceAll(cusPage.getContent(), courseDTO, courseDetail);
                courseEsRepository.save(courseDetail);
            }
            return PageUtil.toPage(cusPage);
        }

        return PageUtil.toPage(courseDTOPage);
    }

    @Override
    public List<CourseDTO> queryAll(CourseQueryCriteria criteria){
        return courseMapper.toDto(courseRepository.findAll((root, criteriaQuery, criteriaBuilder) ->  QueryHelper.andPredicate(root,criteria,criteriaBuilder)));
    }

    @Override
    @Cacheable(key = "'id:' + #p0")
    public CourseDTO findDTOById(Long id) {
        CourseDTO courseDTO = courseEsRepository.findById(id).orElse(null);
        if (courseDTO == null){
            Course course = courseRepository.findById(id).orElse(null);
            ValidUtil.notNull(course, Course.ENTITY_NAME, "id", id);
            courseDTO = courseMapper.toDto(course);
            Section exampleSection = new Section();
            exampleSection.setCourseId(id);
            List<SectionDTO> sectionDTOS = sectionMapper.toDto(sectionRepository.findAll(Example.of(exampleSection)));
            if (sectionDTOS != null && sectionDTOS.size() != 0){
                Collections.sort(sectionDTOS);
                courseDTO.setSections(new LinkedHashSet<>(sectionDTOS));
            }
        }
        return courseDTO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CourseDTO create(CourseSubmitDTO resources) {
        Course course = courseRepository.save(resources.getCourse());
        course.setCode("KC_"+RandomUtil.getRandomString(5)+"_"+course.getId());
        course = courseRepository.save(course);
        CourseDTO courseDTO = courseMapper.toDto(course);
        Set<Section> sections = resources.getSections();
        if (sections != null){
            List sectionList = new LinkedList(sections);
            for (Section section: sections){
                section.setCourseId(courseDTO.getId());
                sectionRepository.save(section);
            }
            List<SectionDTO> sectionDTOS = sectionMapper.toDto(sectionList);
            Collections.sort(sectionDTOS);
            courseDTO.setSections(new LinkedHashSet<>(sectionDTOS));
        }
        courseEsRepository.save(courseDTO);
        return courseDTO;
    }

    @Override
    @CacheEvict(key = "'id:' + #p0.id")
    @Transactional(rollbackFor = Exception.class)
    public void update(CourseSubmitDTO resources) {
        Course course = courseRepository.findById(resources.getCourse().getId()).orElse(null);
        ValidUtil.notNull(course, Course.ENTITY_NAME, "id",resources.getCourse().getId());

        if (resources.getSections() != null){
            Section exampleSection  = new Section();
            exampleSection.setCourseId(resources.getCourse().getId());
            List<Section> sections = sectionRepository.findAll(Example.of(exampleSection));
            sectionRepository.logicDeleteInBatch(sections);

            for (Section section: resources.getSections()){
                section.setCourseId(resources.getCourse().getId());
                sectionRepository.save(section);
            }
        }

        course.copy(resources.getCourse());
        courseEsRepository.deleteById(resources.getCourse().getId());
        courseRepository.save(course);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteAll(Set<Long> ids) {
        for (Long id: ids){
            Section exampleSection  = new Section();
            exampleSection.setCourseId(id);
            List<Section> sections = sectionRepository.findAll(Example.of(exampleSection));
            sectionRepository.logicDeleteInBatch(sections);
            courseEsRepository.deleteById(id);
        }
        redisUtils.delByKeys("course::id:", ids);
        courseRepository.logicDeleteInBatchById(ids);
    }

    @Override
    public void download(List<CourseDTO> queryAll, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (CourseDTO course : queryAll) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("课程名称", course.getName());
            map.put("课程简介", course.getDetail());
            map.put("学分类型", course.getTypeId());
            map.put("学分", course.getCredit());
            map.put("是否必修", course.getCompulsory());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }
}